package jmine.tec.web.front.dao;

import java.util.List;

import jmine.tec.web.front.FrontException;
import jmine.tec.web.front.FrontMessages;
import jmine.tec.web.front.domain.BackOfficeRequest;
import jmine.tec.web.front.domain.BackOfficeRequestStatus;
import jmine.tec.web.front.domain.BackOfficeRequestType;
import jmine.tec.web.front.domain.BackOfficeRequestValue;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Property;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.Subqueries;

import bancosys.tec.persist.dao.BaseDAO;
import bancosys.tec.persist.dao.BeanNotFoundException;
import bancosys.tec.persist.hibernate.RestrictionsUtils;
import bancosys.tec.utils.date.Date;
import bancosys.tec.utils.date.Time;
import bancosys.tec.utils.date.Timestamp;

/**
 * @author lundberg
 */
public class BackOfficeRequestDAO extends BaseDAO<BackOfficeRequest> {

    /**
     * Finds all requests matching the given parameters
     * 
     * @param type type
     * @param date date
     * @param status status
     * @param user user
     * @param propertyFilters properties
     * @return list of matching requests
     */
    public List<BackOfficeRequest> findByTypeDateStatusUserProperties(BackOfficeRequestType type, Date date,
            BackOfficeRequestStatus status, String user, List<BackOfficeRequestValue<?>> propertyFilters) {

        Criteria criteria = this.getSession().createCriteria(this.getPersistableClass(), "requestFilter");
        RestrictionsUtils.addRestrictionEqId(criteria, "type.id", type);
        RestrictionsUtils.addRestrictionEqId(criteria, "status.id", status);
        criteria.add(Restrictions.between("requestTimestamp", new Timestamp(date, new Time(0, 0, 0)),
                new Timestamp(new Date(date).addDays(1), new Time(0, 0, 0))));
        if (!StringUtils.isEmpty(user)) {
            criteria.add(Restrictions.like("requestingUser", user, MatchMode.ANYWHERE));
        }
        criteria.addOrder(Order.desc("requestTimestamp"));

        if (propertyFilters != null) {
            for (BackOfficeRequestValue<?> propertyFilter : propertyFilters) {
                if (propertyFilter.getValue() != null) {
                    DetachedCriteria subFilter = DetachedCriteria.forClass(propertyFilter.getClass(), "subqueryFilter");
                    subFilter.setProjection(Property.forName("subqueryFilter.id"));
                    subFilter.add(Restrictions.eqProperty("subqueryFilter.request.id", "requestFilter.id"));
                    subFilter.add(Restrictions.eq("subqueryFilter.property.id", propertyFilter.getProperty().getId()));
                    subFilter.add(propertyFilter.getCriteriaExpression());
                    criteria.add(Subqueries.exists(subFilter));
                }
            }
        }

        return this.executeQuery(criteria);
    }

    /**
     * @param id Valor que referencia um form.
     * @return {@link BackOfficeRequest} relacionado com o form.
     * @throws BeanNotFoundException Nenhum BackOfficeRequest encontrado.
     */
    public BackOfficeRequest findByFormReferenceValue(Long id) throws BeanNotFoundException {
        Criteria criteria = this.getSession().createCriteria(this.getPersistableClass(), "requestFilter");
        RestrictionsUtils.addRestrictionEq(criteria, "formReferenceValue", id);
        List<BackOfficeRequest> result = this.executeQuery(criteria);
        if (result.size() > 1) {
            throw new FrontException(FrontMessages.TOO_MANY_BACKOFFICEREQUESTS_FOUND.create(result.size(), id));
        } else if (result.size() == 0) {
            throw new BeanNotFoundException(FrontMessages.NO_BACKOFFICEREQUEST_FOUND_FOR_GIVEN_FORM_REFERENCE_VALUE.create(id));
        }
        return result.get(0);
    }
}
